/*
 * Copyright (C) 2008 ZXing authors
 * Copyright 2011 Robert Theis
 * Copyright 2012 Hai Do Minh
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package hpcc.hut.edu.vn.ocr;

import hpcc.hut.edu.vn.ocr.camera.CameraManager;
import hpcc.hut.edu.vn.ocr.camera.GetImageButton;
import hpcc.hut.edu.vn.ocr.camera.ShutterButton;
import hpcc.hut.edu.vn.ocr.language.LanguageCodeHelper;
import hpcc.hut.edu.vn.ocr.language.TranslateBingAsyncTask;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.preference.PreferenceManager;
import android.text.ClipboardManager;
import android.util.Log;
import android.util.TypedValue;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

import com.googlecode.tesseract.android.TessBaseAPI;

/**
 * This activity opens the camera and does the actual scanning on a background
 * thread. It draws a viewfinder to help the user place the barcode correctly,
 * shows feedback as the image processing is happening, and then overlays the
 * results when a scan is successful.
 * 
 * The code for this class was adapted from the ZXing project:
 * http://code.google.com/p/zxing/
 */
public final class CaptureActivity extends Activity implements
		SurfaceHolder.Callback, ShutterButton.OnShutterButtonListener,
		GetImageButton.OnGetImageButtonListener {

	private static final String TAG = CaptureActivity.class.getSimpleName();

	// Note: These constants will be overridden by any default values defined in
	// preferences.xml.

	/** ISO 639-3 language code indicating the default recognition language. */
	public static final String DEFAULT_SOURCE_LANGUAGE_CODE = "eng";

	/**
	 * ISO 639-1 language code indicating the default target language for
	 * translation.
	 */
	public static final String DEFAULT_TARGET_LANGUAGE_CODE = "vi";

	/** The default OCR engine to use. */
	public static final String DEFAULT_OCR_ENGINE_MODE = "Normal OCR";

	/** The default page segmentation mode to use. */
	public static final String DEFAULT_PAGE_SEGMENTATION_MODE = "Auto";

	/** Whether to use autofocus by default. */
	public static final boolean DEFAULT_TOGGLE_AUTO_FOCUS = true;

	/** Whether to beep by default when the shutter button is pressed. */
	public static final boolean DEFAULT_TOGGLE_BEEP = true;

	/** Whether to initially show a looping, real-time OCR display. */
	public static final boolean DEFAULT_TOGGLE_CONTINUOUS = false;

	/** Whether to initially reverse the image returned by the camera. */
	public static final boolean DEFAULT_TOGGLE_REVERSED_IMAGE = false;

	/** Whether to enable the use of online translation services be default. */
	public static final boolean DEFAULT_TOGGLE_TRANSLATION = true;

	/** Whether the light should be initially activated by default. */
	public static final boolean DEFAULT_TOGGLE_LIGHT = false;

	public static final boolean DEFAULT_ONLINE_MODE = false;

	public static final boolean DEFAULT_PRE_POST_PROCESSING = false;

	/**
	 * Flag to display the real-time recognition results at the top of the
	 * scanning screen.
	 */
	private static final boolean CONTINUOUS_DISPLAY_RECOGNIZED_TEXT = true;

	/** Flag to display recognition-related statistics on the scanning screen. */
	private static final boolean CONTINUOUS_DISPLAY_METADATA = true;

	/** Languages for which Cube data is available. */
	static final String[] CUBE_SUPPORTED_LANGUAGES = { "ara", // Arabic
			"eng", // English
			"hin" // Hindi
	};

	/** Languages that require Cube, and cannot run using Tesseract. */
	private static final String[] CUBE_REQUIRED_LANGUAGES = { "ara" // Arabic
	};

	/** Resource to use for data file downloads. */
	static final String DOWNLOAD_BASE = "http://tesseract-ocr.googlecode.com/files/";

	/** Download filename for orientation and script detection (OSD) data. */
	static final String OSD_FILENAME = "tesseract-ocr-3.01.osd.tar";

	/** Destination filename for orientation and script detection (OSD) data. */
	static final String OSD_FILENAME_BASE = "osd.traineddata";

	/**
	 * Minimum mean confidence score necessary to not reject single-shot OCR
	 * result. Currently unused.
	 */
	static final int MINIMUM_MEAN_CONFIDENCE = 0; // 0 means don't reject any
													// scored results

	// Context menu
	private static final int SETTINGS_ID = Menu.FIRST;

	// Options menu, for copy to clipboard
	public static final int OPTIONS_COPY_RECOGNIZED_TEXT_ID = Menu.FIRST;
	public static final int OPTIONS_COPY_TRANSLATED_TEXT_ID = Menu.FIRST + 1;
	public static final int OPTIONS_SHARE_RECOGNIZED_TEXT_ID = Menu.FIRST + 2;
	public static final int OPTIONS_SHARE_TRANSLATED_TEXT_ID = Menu.FIRST + 3;
	public static final int OPTIONS_SAVE_RECOGNIZED_TEXT_ID = Menu.FIRST + 4;
	public static final int OPTIONS_SAVE_TRANSLATED_TEXT_ID = Menu.FIRST + 5;

	private CameraManager cameraManager;
	private CaptureActivityHandler handler;
	private ViewfinderView viewfinderView;
	private SurfaceView surfaceView;
	private SurfaceHolder surfaceHolder;
	private TextView statusViewBottom;
	private TextView statusViewTop;
	private TextView ocrResultView;
	private TextView translationView;
	private View cameraButtonView;
	private View resultView;
	private View progressView;
	private OcrResult lastResult;
	private Bitmap lastBitmap;
	private boolean hasSurface;
	private TessBaseAPI baseApi; // Java interface for the Tesseract OCR engine
	private String sourceLanguageCodeOcr; // ISO 639-3 language code
	private String sourceLanguageReadable; // Language name, for example,
											// "English"
	private String sourceLanguageCodeTranslation; // ISO 639-1 language code
	private String targetLanguageCodeTranslation; // ISO 639-1 language code
	private String targetLanguageReadable; // Language name, for example,
											// "English"
	private int pageSegmentationMode = TessBaseAPI.PSM_AUTO;
	private int ocrEngineMode = TessBaseAPI.OEM_TESSERACT_ONLY;
	private String characterBlacklist;
	private String characterWhitelist;
	private ShutterButton shutterButton;
	private GetImageButton getimgButton;
	private boolean isTranslationActive; // Whether we want to show translations
	private boolean isOnline;
	private boolean isPrePostProcessing;
	private boolean isContinuousModeActive; // Whether we are doing OCR in
											// continuous mode
	private boolean isEditResult;
	private SharedPreferences prefs;
	private OnSharedPreferenceChangeListener listener;
	private ProgressDialog dialog; // for initOcr - language download & unzip
	private ProgressDialog indeterminateDialog; // also for initOcr - init OCR
												// engine
	private boolean isEngineReady;
	private boolean isPaused;

	private String dirPath;

	// private static boolean isFirstLaunch; // True if this is the first time
	// the app is being run

	Handler getHandler() {
		return handler;
	}

	TessBaseAPI getBaseApi() {
		return baseApi;
	}

	CameraManager getCameraManager() {
		return cameraManager;
	}

	boolean isOCROnline() {
		return isOnline;
	}

	boolean isPrePostProcessing() {
		return isPrePostProcessing;
	}

	boolean isEditResult() {
		return isEditResult;
	}

	String getSourceLanguageCode() {
		return sourceLanguageCodeOcr;
	}

	int getPageSegmentationMode() {
		return pageSegmentationMode;
	}

	@Override
	public void onCreate(Bundle icicle) {
		super.onCreate(icicle);

		if (checkFirstLaunch()) {
			setDefaultPreferences();
		}

		Window window = getWindow();
		window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
		requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
		setContentView(R.layout.capture);
		viewfinderView = (ViewfinderView) findViewById(R.id.viewfinder_view);
		cameraButtonView = findViewById(R.id.camera_button_view);
		resultView = findViewById(R.id.result_view);

		statusViewBottom = (TextView) findViewById(R.id.status_view_bottom);
		registerForContextMenu(statusViewBottom);
		statusViewTop = (TextView) findViewById(R.id.status_view_top);
		registerForContextMenu(statusViewTop);

		dirPath = Environment.getExternalStorageDirectory().getAbsolutePath()
				+ "/BKDroidOCR/";
		File dir = new File(dirPath);
		if (!dir.exists()) {
			dir.mkdir();
		}

		handler = null;
		lastResult = null;
		hasSurface = false;

		// Camera shutter button
		shutterButton = (ShutterButton) findViewById(R.id.shutter_button);
		shutterButton.setOnShutterButtonListener(this);
		// get Image from SD card
		getimgButton = (GetImageButton) findViewById(R.id.getimg_button);
		getimgButton.setOnGetImageButtonListener(this);

		ocrResultView = (TextView) findViewById(R.id.ocr_result_text_view);
		registerForContextMenu(ocrResultView);
		translationView = (TextView) findViewById(R.id.translation_text_view);
		registerForContextMenu(translationView);

		progressView = (View) findViewById(R.id.indeterminate_progress_indicator_view);

		cameraManager = new CameraManager(getApplication());
		viewfinderView.setCameraManager(cameraManager);

		// Set listener to change the size of the viewfinder rectangle.
		viewfinderView.setOnTouchListener(new View.OnTouchListener() {
			int lastX = -1;
			int lastY = -1;

			@Override
			public boolean onTouch(View v, MotionEvent event) {
				switch (event.getAction()) {
				case MotionEvent.ACTION_DOWN:
					lastX = -1;
					lastY = -1;
					return true;
				case MotionEvent.ACTION_MOVE:
					int currentX = (int) event.getX();
					int currentY = (int) event.getY();

					try {
						Rect rect = cameraManager.getFramingRect();

						final int BUFFER = 50;
						final int BIG_BUFFER = 60;
						if (lastX >= 0) {
							// Adjust the size of the viewfinder rectangle.
							// Check if the touch event occurs in the corner
							// areas first, because the regions overlap.
							if (((currentX >= rect.left - BIG_BUFFER && currentX <= rect.left
									+ BIG_BUFFER) || (lastX >= rect.left
									- BIG_BUFFER && lastX <= rect.left
									+ BIG_BUFFER))
									&& ((currentY <= rect.top + BIG_BUFFER && currentY >= rect.top
											- BIG_BUFFER) || (lastY <= rect.top
											+ BIG_BUFFER && lastY >= rect.top
											- BIG_BUFFER))) {
								// Top left corner: adjust both top and left
								// sides
								cameraManager.adjustFramingRect(
										2 * (lastX - currentX),
										2 * (lastY - currentY));
								viewfinderView.removeResultText();
							} else if (((currentX >= rect.right - BIG_BUFFER && currentX <= rect.right
									+ BIG_BUFFER) || (lastX >= rect.right
									- BIG_BUFFER && lastX <= rect.right
									+ BIG_BUFFER))
									&& ((currentY <= rect.top + BIG_BUFFER && currentY >= rect.top
											- BIG_BUFFER) || (lastY <= rect.top
											+ BIG_BUFFER && lastY >= rect.top
											- BIG_BUFFER))) {
								// Top right corner: adjust both top and right
								// sides
								cameraManager.adjustFramingRect(
										2 * (currentX - lastX),
										2 * (lastY - currentY));
								viewfinderView.removeResultText();
							} else if (((currentX >= rect.left - BIG_BUFFER && currentX <= rect.left
									+ BIG_BUFFER) || (lastX >= rect.left
									- BIG_BUFFER && lastX <= rect.left
									+ BIG_BUFFER))
									&& ((currentY <= rect.bottom + BIG_BUFFER && currentY >= rect.bottom
											- BIG_BUFFER) || (lastY <= rect.bottom
											+ BIG_BUFFER && lastY >= rect.bottom
											- BIG_BUFFER))) {
								// Bottom left corner: adjust both bottom and
								// left sides
								cameraManager.adjustFramingRect(
										2 * (lastX - currentX),
										2 * (currentY - lastY));
								viewfinderView.removeResultText();
							} else if (((currentX >= rect.right - BIG_BUFFER && currentX <= rect.right
									+ BIG_BUFFER) || (lastX >= rect.right
									- BIG_BUFFER && lastX <= rect.right
									+ BIG_BUFFER))
									&& ((currentY <= rect.bottom + BIG_BUFFER && currentY >= rect.bottom
											- BIG_BUFFER) || (lastY <= rect.bottom
											+ BIG_BUFFER && lastY >= rect.bottom
											- BIG_BUFFER))) {
								// Bottom right corner: adjust both bottom and
								// right sides
								cameraManager.adjustFramingRect(
										2 * (currentX - lastX),
										2 * (currentY - lastY));
								viewfinderView.removeResultText();
							} else if (((currentX >= rect.left - BUFFER && currentX <= rect.left
									+ BUFFER) || (lastX >= rect.left - BUFFER && lastX <= rect.left
									+ BUFFER))
									&& ((currentY <= rect.bottom && currentY >= rect.top) || (lastY <= rect.bottom && lastY >= rect.top))) {
								// Adjusting left side: event falls within
								// BUFFER pixels of left side, and between top
								// and bottom side limits
								cameraManager.adjustFramingRect(
										2 * (lastX - currentX), 0);
								viewfinderView.removeResultText();
							} else if (((currentX >= rect.right - BUFFER && currentX <= rect.right
									+ BUFFER) || (lastX >= rect.right - BUFFER && lastX <= rect.right
									+ BUFFER))
									&& ((currentY <= rect.bottom && currentY >= rect.top) || (lastY <= rect.bottom && lastY >= rect.top))) {
								// Adjusting right side: event falls within
								// BUFFER pixels of right side, and between top
								// and bottom side limits
								cameraManager.adjustFramingRect(
										2 * (currentX - lastX), 0);
								viewfinderView.removeResultText();
							} else if (((currentY <= rect.top + BUFFER && currentY >= rect.top
									- BUFFER) || (lastY <= rect.top + BUFFER && lastY >= rect.top
									- BUFFER))
									&& ((currentX <= rect.right && currentX >= rect.left) || (lastX <= rect.right && lastX >= rect.left))) {
								// Adjusting top side: event falls within BUFFER
								// pixels of top side, and between left and
								// right side limits
								cameraManager.adjustFramingRect(0,
										2 * (lastY - currentY));
								viewfinderView.removeResultText();
							} else if (((currentY <= rect.bottom + BUFFER && currentY >= rect.bottom
									- BUFFER) || (lastY <= rect.bottom + BUFFER && lastY >= rect.bottom
									- BUFFER))
									&& ((currentX <= rect.right && currentX >= rect.left) || (lastX <= rect.right && lastX >= rect.left))) {
								// Adjusting bottom side: event falls within
								// BUFFER pixels of bottom side, and between
								// left and right side limits
								cameraManager.adjustFramingRect(0,
										2 * (currentY - lastY));
								viewfinderView.removeResultText();
							}
						}
					} catch (NullPointerException e) {
						Log.e(TAG, "Framing rect not available", e);
					}
					v.invalidate();
					lastX = currentX;
					lastY = currentY;
					return true;
				case MotionEvent.ACTION_UP:
					lastX = -1;
					lastY = -1;
					return true;
				}
				return false;
			}
		});

		isEngineReady = false;
	}

	@Override
	protected void onResume() {
		super.onResume();
		resetStatusView();

		if (!this.getimgButton.isClickable()) {
			this.setGetimgButtonClickable(true);
		}

		String previousSourceLanguageCodeOcr = sourceLanguageCodeOcr;
		int previousOcrEngineMode = ocrEngineMode;

		retrievePreferences();

		// Set up the camera preview surface.
		surfaceView = (SurfaceView) findViewById(R.id.preview_view);
		surfaceHolder = surfaceView.getHolder();
		if (!hasSurface) {
			surfaceHolder.addCallback(this);
			surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
		}

		if (!this.isOnline) {
			// Comment out the following block to test non-OCR functions without
			// an
			// SD card

			// Do OCR engine initialization, if necessary
			boolean doNewInit = (baseApi == null)
					|| !sourceLanguageCodeOcr
							.equals(previousSourceLanguageCodeOcr)
					|| ocrEngineMode != previousOcrEngineMode;
			if (doNewInit) {
				// Initialize the OCR engine
				File storageDirectory = getStorageDirectory();
				if (storageDirectory != null) {
					initOcrEngine(storageDirectory, sourceLanguageCodeOcr,
							sourceLanguageReadable);
				}
			} else {
				// We already have the engine initialized, so just start the
				// camera.
				resumeOCR(true);
			}
		} else {
			// Online
			resumeOCR(false);
		}
	}

	/**
	 * Method to start or restart recognition after the OCR engine has been
	 * initialized, or after the app regains focus. Sets state related settings
	 * and OCR engine parameters, and requests camera initialization.
	 */
	void resumeOCR(boolean isOffline) {
		Log.d(TAG, "resumeOCR()");

		// This method is called when Tesseract has already been successfully
		// initialized, so set
		// isEngineReady = true here.
		isEngineReady = true;

		isPaused = false;

		if (handler != null) {
			handler.resetState();
		}
		if (isOffline && baseApi != null) {
			baseApi.setPageSegMode(pageSegmentationMode);
			baseApi.setVariable(TessBaseAPI.VAR_CHAR_BLACKLIST,
					characterBlacklist);
			baseApi.setVariable(TessBaseAPI.VAR_CHAR_WHITELIST,
					characterWhitelist);
		}

		if (hasSurface) {
			// The activity was paused but not stopped, so the surface still
			// exists. Therefore
			// surfaceCreated() won't be called, so init the camera here.
			initCamera(surfaceHolder);
		}
	}

	/** Called when the shutter button is pressed in continuous mode. */
	void onShutterButtonPressContinuous() {
		isPaused = true;
		handler.stop();
		if (lastResult != null) {
			handleOcrDecode(lastResult);
		} else {
			Toast toast = Toast.makeText(this, "OCR failed. Please try again.",
					Toast.LENGTH_SHORT);
			toast.setGravity(Gravity.TOP, 0, 0);
			toast.show();
			resumeContinuousDecoding();
		}
	}

	/** Called to resume recognition after translation in continuous mode. */
	void resumeContinuousDecoding() {
		isPaused = false;
		resetStatusView();
		setStatusViewForContinuous();
		DecodeHandler.resetDecodeState();
		handler.resetState();
		if (shutterButton != null) {
			shutterButton.setVisibility(View.VISIBLE);
		}
		if (getimgButton != null) {
			getimgButton.setVisibility(View.VISIBLE);
		}
	}

	@Override
	public void surfaceCreated(SurfaceHolder holder) {
		Log.d(TAG, "surfaceCreated()");

		if (holder == null) {
			Log.e(TAG, "surfaceCreated gave us a null surface");
		}

		// Only initialize the camera if the OCR engine is ready to go.
		if (!hasSurface && isEngineReady) {
			Log.d(TAG, "surfaceCreated(): calling initCamera()...");
			initCamera(holder);
		}
		hasSurface = true;
	}

	/** Initializes the camera and starts the handler to begin previewing. */
	private void initCamera(SurfaceHolder surfaceHolder) {
		Log.d(TAG, "initCamera()");
		if (surfaceHolder == null) {
			throw new IllegalStateException("No SurfaceHolder provided");
		}
		try {

			// Open and initialize the camera
			cameraManager.openDriver(surfaceHolder);

			// Creating the handler starts the preview, which can also throw a
			// RuntimeException.
			handler = new CaptureActivityHandler(this, cameraManager,
					isContinuousModeActive);

		} catch (IOException ioe) {
			showErrorMessage("Error",
					"Could not initialize camera. Please try restarting device.");
		} catch (RuntimeException e) {
			// Barcode Scanner has seen crashes in the wild of this variety:
			// java.?lang.?RuntimeException: Fail to connect to camera service
			showErrorMessage("Error",
					"Could not initialize camera. Please try restarting device.");
		}
	}

	@Override
	protected void onPause() {
		if (handler != null) {
			handler.quitSynchronously();
		}

		// Stop using the camera, to avoid conflicting with other camera-based
		// apps
		cameraManager.closeDriver();

		if (!hasSurface) {
			SurfaceView surfaceView = (SurfaceView) findViewById(R.id.preview_view);
			SurfaceHolder surfaceHolder = surfaceView.getHolder();
			surfaceHolder.removeCallback(this);
		}
		super.onPause();
	}

	void stopHandler() {
		if (handler != null) {
			handler.stop();
		}
	}

	@Override
	protected void onDestroy() {
		if (baseApi != null) {
			baseApi.end();
		}
		super.onDestroy();

		Runtime.getRuntime().gc();
		android.os.Process.killProcess(android.os.Process.myPid());
	}

	@Override
	public boolean onKeyDown(int keyCode, KeyEvent event) {
		if (keyCode == KeyEvent.KEYCODE_BACK) {

			// First check if we're paused in continuous mode, and if so, just
			// unpause.
			if (isPaused) {
				Log.d(TAG,
						"only resuming continuous recognition, not quitting...");
				resumeContinuousDecoding();
				return true;
			}

			// Exit the app if we're not viewing an OCR result.
			if (lastResult == null) {
				AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(
						this);
				// set dialog message
				alertDialogBuilder
						.setMessage("Do you want to exit?")
						.setCancelable(false)
						.setPositiveButton("Exit",
								new DialogInterface.OnClickListener() {
									public void onClick(DialogInterface dialog,
											int id) {
										// if this button is clicked, close
										// current activity
										CaptureActivity.this
												.setResult(RESULT_CANCELED);
										CaptureActivity.this.finish();
									}
								})
						.setNegativeButton("Cancel",
								new DialogInterface.OnClickListener() {
									public void onClick(DialogInterface dialog,
											int id) {
										// if this button is clicked, just close
										// the dialog box and do nothing
										dialog.cancel();
									}
								});

				// create alert dialog
				AlertDialog alertDialog = alertDialogBuilder.create();

				// show it
				alertDialog.show();
				return true;
			} else {
				// Go back to previewing in regular OCR mode.
				resetStatusView();
				if (handler != null) {
					handler.sendEmptyMessage(R.id.restart_preview);
				}
				return true;
			}
		} else if (keyCode == KeyEvent.KEYCODE_CAMERA) {
			if (isContinuousModeActive) {
				onShutterButtonPressContinuous();
			} else {
				handler.hardwareShutterButtonClick();
			}
			return true;
		} else if (keyCode == KeyEvent.KEYCODE_FOCUS) {
			// Only perform autofocus if user is not holding down the button.
			if (event.getRepeatCount() == 0) {
				cameraManager.requestAutoFocus(500L);
			}
			return true;
		}
		return super.onKeyDown(keyCode, event);
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// MenuInflater inflater = getMenuInflater();
		// inflater.inflate(R.menu.options_menu, menu);
		super.onCreateOptionsMenu(menu);
		menu.add(0, SETTINGS_ID, 0, "Settings").setIcon(
				android.R.drawable.ic_menu_preferences);
		return true;
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		Intent intent;
		switch (item.getItemId()) {
		case SETTINGS_ID: {
			intent = new Intent().setClass(this, PreferencesActivity.class);
			startActivity(intent);
			break;
		}
		}
		return super.onOptionsItemSelected(item);
	}

	public void surfaceDestroyed(SurfaceHolder holder) {
		hasSurface = false;
	}

	public void surfaceChanged(SurfaceHolder holder, int format, int width,
			int height) {
	}

	/** Sets the necessary language code values for the given OCR language. */
	private boolean setSourceLanguage(String languageCode) {
		sourceLanguageCodeOcr = languageCode;
		sourceLanguageCodeTranslation = LanguageCodeHelper
				.mapLanguageCode(languageCode);
		sourceLanguageReadable = LanguageCodeHelper.getOcrLanguageName(this,
				languageCode);
		return true;
	}

	/**
	 * Sets the necessary language code values for the translation target
	 * language.
	 */
	private boolean setTargetLanguage(String languageCode) {
		targetLanguageCodeTranslation = languageCode;
		targetLanguageReadable = LanguageCodeHelper.getTranslationLanguageName(
				this, languageCode);
		return true;
	}

	/** Finds the proper location on the SD card where we can save files. */
	private File getStorageDirectory() {
		// Log.d(TAG, "getStorageDirectory(): API level is " +
		// Integer.valueOf(android.os.Build.VERSION.SDK_INT));

		String state = null;
		try {
			state = Environment.getExternalStorageState();
		} catch (RuntimeException e) {
			Log.e(TAG, "Is the SD card visible?", e);
			showErrorMessage("Error",
					"Required external storage (such as an SD card) is unavailable.");
		}

		if (Environment.MEDIA_MOUNTED.equals(Environment
				.getExternalStorageState())) {

			// We can read and write the media
			// if (Integer.valueOf(android.os.Build.VERSION.SDK_INT) > 7) {
			// For Android 2.2 and above

			try {
				return getExternalFilesDir(Environment.MEDIA_MOUNTED);
			} catch (NullPointerException e) {
				// We get an error here if the SD card is visible, but full
				Log.e(TAG, "External storage is unavailable");
				showErrorMessage("Error",
						"Required external storage (such as an SD card) is full or unavailable.");
			}

			// } else {
			// // For Android 2.1 and below, explicitly give the path as, for
			// example,
			// // "/mnt/sdcard/Android/data/edu.sfsu.cs.orange.ocr/files/"
			// return new
			// File(Environment.getExternalStorageDirectory().toString() +
			// File.separator +
			// "Android" + File.separator + "data" + File.separator +
			// getPackageName() +
			// File.separator + "files" + File.separator);
			// }

		} else if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
			// We can only read the media
			Log.e(TAG, "External storage is read-only");
			showErrorMessage(
					"Error",
					"Required external storage (such as an SD card) is unavailable for data storage.");
		} else {
			// Something else is wrong. It may be one of many other states, but
			// all we need
			// to know is we can neither read nor write
			Log.e(TAG, "External storage is unavailable");
			showErrorMessage("Error",
					"Required external storage (such as an SD card) is unavailable or corrupted.");
		}
		return null;
	}

	/**
	 * Requests initialization of the OCR engine with the given parameters.
	 * 
	 * @param storageRoot
	 *            Path to location of the tessdata directory to use
	 * @param languageCode
	 *            Three-letter ISO 639-3 language code for OCR
	 * @param languageName
	 *            Name of the language for OCR, for example, "English"
	 */
	private void initOcrEngine(File storageRoot, String languageCode,
			String languageName) {
		isEngineReady = false;

		// Set up the dialog box for the thermometer-style download progress
		// indicator
		if (dialog != null) {
			dialog.dismiss();
		}
		dialog = new ProgressDialog(this);

		// If we have a language that only runs using Cube, then set the
		// ocrEngineMode to Cube
		if (ocrEngineMode != TessBaseAPI.OEM_CUBE_ONLY) {
			for (String s : CUBE_REQUIRED_LANGUAGES) {
				if (s.equals(languageCode)) {
					ocrEngineMode = TessBaseAPI.OEM_CUBE_ONLY;
					SharedPreferences prefs = PreferenceManager
							.getDefaultSharedPreferences(this);
					prefs.edit()
							.putString(PreferencesActivity.KEY_OCR_ENGINE_MODE,
									getOcrEngineModeName()).commit();
				}
			}
		}

		// If our language doesn't support Cube, then set the ocrEngineMode to
		// Tesseract
		if (ocrEngineMode != TessBaseAPI.OEM_TESSERACT_ONLY) {
			boolean cubeOk = false;
			for (String s : CUBE_SUPPORTED_LANGUAGES) {
				if (s.equals(languageCode)) {
					cubeOk = true;
				}
			}
			if (!cubeOk) {
				ocrEngineMode = TessBaseAPI.OEM_TESSERACT_ONLY;
				SharedPreferences prefs = PreferenceManager
						.getDefaultSharedPreferences(this);
				prefs.edit()
						.putString(PreferencesActivity.KEY_OCR_ENGINE_MODE,
								getOcrEngineModeName()).commit();
			}
		}

		// Display the name of the OCR engine we're initializing in the
		// indeterminate progress dialog box
		indeterminateDialog = new ProgressDialog(this);
		indeterminateDialog.setTitle("Please wait");
		// String ocrEngineModeName = getOcrEngineModeName();
		indeterminateDialog.setMessage("Initializing for " + languageName
				+ "...");
		// if (ocrEngineModeName.equals("Both")) {
		// indeterminateDialog
		// .setMessage("Initializing Cube and Tesseract OCR engines for "
		// + languageName + "...");
		// } else {
		// indeterminateDialog.setMessage("Initializing " + ocrEngineModeName
		// + " OCR engine for " + languageName + "...");
		// }
		indeterminateDialog.setCancelable(false);
		indeterminateDialog.show();

		if (handler != null) {
			handler.quitSynchronously();
		}

		// Disable continuous mode if we're using Cube. This will prevent bad
		// states for devices
		// with low memory that crash when running OCR with Cube, and prevent
		// unwanted delays.
		if (ocrEngineMode == TessBaseAPI.OEM_CUBE_ONLY
				|| ocrEngineMode == TessBaseAPI.OEM_TESSERACT_CUBE_COMBINED) {
			Log.d(TAG, "Disabling continuous preview");
			isContinuousModeActive = false;
			SharedPreferences prefs = PreferenceManager
					.getDefaultSharedPreferences(this);
			prefs.edit().putBoolean(PreferencesActivity.KEY_CONTINUOUS_PREVIEW,
					false);
		}

		// Start AsyncTask to install language data and init OCR
		baseApi = new TessBaseAPI();
		new OcrInitAsyncTask(this, baseApi, dialog, indeterminateDialog,
				languageCode, languageName, ocrEngineMode).execute(storageRoot
				.toString());
	}

	/**
	 * Displays information relating to the result of OCR, and requests a
	 * translation if necessary.
	 * 
	 * @param ocrResult
	 *            Object representing successful OCR results
	 * @return True if a non-null result was received for OCR
	 */
	boolean handleOcrDecode(OcrResult ocrResult) {
		lastResult = ocrResult;

		// Test whether the result is null
		if (ocrResult.getText() == null || ocrResult.getText().equals("")) {
			Toast toast = Toast.makeText(this, "OCR error. Please try again!",
					Toast.LENGTH_SHORT);
			toast.setGravity(Gravity.TOP, 0, 0);
			toast.show();
			return false;
		}

		// System.err.println ("CaptureAc: result: " + ocrResult.getText());

		if (!this.isEditResult) {

			// Turn off capture-related UI elements
			shutterButton.setVisibility(View.GONE);
			getimgButton.setVisibility(View.GONE);
			statusViewBottom.setVisibility(View.GONE);
			statusViewTop.setVisibility(View.GONE);
			cameraButtonView.setVisibility(View.GONE);
			viewfinderView.setVisibility(View.GONE);
			resultView.setVisibility(View.VISIBLE);

			ImageView bitmapImageView = (ImageView) findViewById(R.id.image_view);
			lastBitmap = ocrResult.getBitmap();
			if (lastBitmap == null) {
				bitmapImageView.setImageBitmap(BitmapFactory.decodeResource(
						getResources(), R.drawable.ic_launcher));
			} else {
				bitmapImageView.setImageBitmap(lastBitmap);
			}

			// Display the recognized text
			TextView sourceLanguageTextView = (TextView) findViewById(R.id.source_language_text_view);
			sourceLanguageTextView.setText(sourceLanguageReadable);
			TextView ocrResultTextView = (TextView) findViewById(R.id.ocr_result_text_view);
			ocrResultTextView.setText(ocrResult.getText());
			// Crudely scale betweeen 22 and 32 -- bigger font for shorter text
			int scaledSize = Math
					.max(22, 32 - ocrResult.getText().length() / 4);
			ocrResultTextView.setTextSize(TypedValue.COMPLEX_UNIT_SP,
					scaledSize);

			TextView translationLanguageLabelTextView = (TextView) findViewById(R.id.translation_language_label_text_view);
			TextView translationLanguageTextView = (TextView) findViewById(R.id.translation_language_text_view);
			TextView translationTextView = (TextView) findViewById(R.id.translation_text_view);
			if (isTranslationActive) {
				// Handle translation text fields
				translationLanguageLabelTextView.setVisibility(View.VISIBLE);
				translationLanguageTextView.setText(targetLanguageReadable);
				translationLanguageTextView.setTypeface(
						Typeface.defaultFromStyle(Typeface.NORMAL),
						Typeface.NORMAL);
				translationLanguageTextView.setVisibility(View.VISIBLE);

				// Activate/re-activate the indeterminate progress indicator
				translationTextView.setVisibility(View.GONE);
				progressView.setVisibility(View.VISIBLE);
				setProgressBarVisibility(true);

				// Get the translation asynchronously
				new TranslateBingAsyncTask(this, sourceLanguageCodeTranslation,
						targetLanguageCodeTranslation, ocrResult.getText())
						.execute();
			} else {
				translationLanguageLabelTextView.setVisibility(View.GONE);
				translationLanguageTextView.setVisibility(View.GONE);
				translationTextView.setVisibility(View.GONE);
				progressView.setVisibility(View.GONE);
				setProgressBarVisibility(false);
			}
		} else {
			Intent intent = new Intent().setClass(this, ResultActivity.class);
			intent.putExtra(Database.HEADER_OCR_LANGUAGE,
					sourceLanguageReadable);
			intent.putExtra(Database.HEADER_OCR_RESULT, ocrResult.getText());
			intent.putExtra(Database.HEADER_OCR_LANGUAGE_CODE,
					sourceLanguageCodeTranslation);
			intent.putExtra(Database.HEADER_TRANSLATE_INDEX, 8);
			startActivity(intent);
		}
		return true;
	}

	/**
	 * Displays information relating to the results of a successful real-time
	 * OCR request.
	 * 
	 * @param ocrResult
	 *            Object representing successful OCR results
	 */
	void handleOcrContinuousDecode(OcrResult ocrResult) {

		if (ocrResult != null && ocrResult.getText() != null) {
			lastResult = ocrResult;

			// Send an OcrResultText object to the ViewfinderView for text
			// rendering
			viewfinderView.addOcrResult(ocrResult);

			Integer meanConfidence = ocrResult.getMeanConfidence();

			if (CONTINUOUS_DISPLAY_RECOGNIZED_TEXT) {
				// Display the recognized text on the screen
				statusViewTop.setText(ocrResult.getText());
				int scaledSize = Math.max(22,
						32 - ocrResult.getText().length() / 4);
				statusViewTop.setTextSize(TypedValue.COMPLEX_UNIT_SP,
						scaledSize);
				statusViewTop.setTextColor(Color.BLACK);
				statusViewTop
						.setBackgroundResource(R.color.status_top_text_background);

				statusViewTop.getBackground().setAlpha(
						meanConfidence * (255 / 100));
			}
		} else {
			lastResult = null;
			viewfinderView.removeResultText();

			// Reset the text in the recognized text box.
			statusViewTop.setText("");
		}
	}

	@Override
	public void onCreateContextMenu(ContextMenu menu, View v,
			ContextMenuInfo menuInfo) {
		super.onCreateContextMenu(menu, v, menuInfo);
		if (v.equals(ocrResultView)) {
			menu.add(Menu.NONE, OPTIONS_COPY_RECOGNIZED_TEXT_ID, Menu.NONE,
					"Copy recognized text");
			menu.add(Menu.NONE, OPTIONS_SHARE_RECOGNIZED_TEXT_ID, Menu.NONE,
					"Share recognized text");
			menu.add(Menu.NONE, OPTIONS_SAVE_RECOGNIZED_TEXT_ID, Menu.NONE,
					"Save recognized text");
		} else if (v.equals(translationView)) {
			menu.add(Menu.NONE, OPTIONS_COPY_TRANSLATED_TEXT_ID, Menu.NONE,
					"Copy translated text");
			menu.add(Menu.NONE, OPTIONS_SHARE_TRANSLATED_TEXT_ID, Menu.NONE,
					"Share translated text");
			menu.add(Menu.NONE, OPTIONS_SAVE_TRANSLATED_TEXT_ID, Menu.NONE,
					"Save translated text");
		}
	}

	@Override
	public boolean onContextItemSelected(MenuItem item) {
		switch (item.getItemId()) {
		case OPTIONS_COPY_RECOGNIZED_TEXT_ID:
		case OPTIONS_COPY_TRANSLATED_TEXT_ID:
			CharSequence text = (item.getItemId() == OPTIONS_COPY_RECOGNIZED_TEXT_ID) ? ocrResultView
					.getText() : translationView.getText();
			this.doOptionsCopyText(text);
			return true;
		case OPTIONS_SHARE_RECOGNIZED_TEXT_ID:
		case OPTIONS_SHARE_TRANSLATED_TEXT_ID:
			text = (item.getItemId() == OPTIONS_SHARE_RECOGNIZED_TEXT_ID) ? ocrResultView
					.getText() : translationView.getText();
			this.doOptionsShareText(
					item.getItemId() == OPTIONS_SAVE_RECOGNIZED_TEXT_ID, text);
			return true;
		case OPTIONS_SAVE_RECOGNIZED_TEXT_ID:
		case OPTIONS_SAVE_TRANSLATED_TEXT_ID:
			text = (item.getItemId() == OPTIONS_SAVE_RECOGNIZED_TEXT_ID) ? ocrResultView
					.getText() : translationView.getText();
			this.doOptionsSaveText(
					item.getItemId() == OPTIONS_SHARE_RECOGNIZED_TEXT_ID, text);
			return true;
		default:
			return super.onContextItemSelected(item);
		}
	}

	public void doOptionsCopyText(final CharSequence text) {
		ClipboardManager clipboardManager = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
		clipboardManager.setText(text);
		if (clipboardManager.hasText()) {
			Toast toast = Toast.makeText(this, "Text copied.",
					Toast.LENGTH_LONG);
			toast.setGravity(Gravity.BOTTOM, 0, 0);
			toast.show();
		}
	}

	public void doOptionsShareText(final boolean isOCR, final CharSequence text) {
		Intent sharingIntent = new Intent(Intent.ACTION_SEND);
		sharingIntent.setType("text/plain");
		sharingIntent.putExtra(Intent.EXTRA_SUBJECT, "Share "
				+ (isOCR ? "OCR Result" : "Translate Result"));
		sharingIntent.putExtra(Intent.EXTRA_TEXT, text.toString());
		startActivity(Intent.createChooser(sharingIntent, "Share via"));
	}

	public void doOptionsSaveText(final boolean isOCR, final CharSequence text) {
		String filename = (isOCR ? "ocr_" : "translate_")
				+ System.currentTimeMillis() + ".txt";
		File file = new File(this.dirPath, filename);
		FileOutputStream fos;
		byte[] bytedata = text.toString().getBytes();
		try {
			fos = new FileOutputStream(file);
			fos.write(bytedata);
			fos.flush();
			fos.close();
			Toast toast = Toast.makeText(this, "Text save as " + this.dirPath
					+ filename + ".", Toast.LENGTH_LONG);
			toast.setGravity(Gravity.BOTTOM, 0, 0);
			toast.show();
		} catch (FileNotFoundException e) {
			Toast toast = Toast.makeText(this,
					"Not found dir path. Can't save text.", Toast.LENGTH_LONG);
			toast.setGravity(Gravity.BOTTOM, 0, 0);
			toast.show();
		} catch (IOException e) {
			Toast toast = Toast.makeText(this, "Write erro! Can't save text.",
					Toast.LENGTH_LONG);
			toast.setGravity(Gravity.BOTTOM, 0, 0);
			toast.show();
		}
	}

	/**
	 * Resets view elements.
	 */
	private void resetStatusView() {
		resultView.setVisibility(View.GONE);
		if (CONTINUOUS_DISPLAY_METADATA) {
			statusViewBottom.setText("");
			statusViewBottom.setTextSize(14);
			statusViewBottom.setTextColor(getResources().getColor(
					R.color.status_text));
			statusViewBottom.setVisibility(View.VISIBLE);
		}
		if (CONTINUOUS_DISPLAY_RECOGNIZED_TEXT) {
			statusViewTop.setText("");
			statusViewTop.setTextSize(14);
			statusViewTop.setVisibility(View.VISIBLE);
		}
		viewfinderView.setVisibility(View.VISIBLE);
		cameraButtonView.setVisibility(View.VISIBLE);
		shutterButton.setVisibility(View.VISIBLE);
		getimgButton.setVisibility(View.VISIBLE);
		lastResult = null;
		viewfinderView.removeResultText();
	}

	/**
	 * Displays a pop-up message showing the name of the current OCR source
	 * language.
	 */
	void showLanguageName() {
		Toast toast = Toast.makeText(this, "OCR: " + sourceLanguageReadable,
				Toast.LENGTH_LONG);
		toast.setGravity(Gravity.TOP, 0, 0);
		toast.show();
	}

	/**
	 * Displays an initial message to the user while waiting for the first OCR
	 * request to be completed after starting realtime OCR.
	 */
	void setStatusViewForContinuous() {
		viewfinderView.removeResultText();
		if (CONTINUOUS_DISPLAY_METADATA) {
			statusViewBottom.setText("OCR Language: " + sourceLanguageReadable
					+ ". Detecting...");
		}
	}

	public void setButtonVisibility(boolean visible) {
		if (shutterButton != null && visible == true) {
			shutterButton.setVisibility(View.VISIBLE);
		} else if (shutterButton != null) {
			shutterButton.setVisibility(View.GONE);
		}
		if (getimgButton != null && visible == true) {
			getimgButton.setVisibility(View.VISIBLE);
		} else if (getimgButton != null) {
			getimgButton.setVisibility(View.GONE);
		}
	}

	/**
	 * Enables/disables the shutter button to prevent double-clicks on the
	 * button.
	 * 
	 * @param clickable
	 *            True if the button should accept a click
	 */
	void setShutterButtonClickable(boolean clickable) {
		shutterButton.setClickable(clickable);
	}

	/**
	 * Enables/disables the getimage button to prevent double-clicks on the
	 * button.
	 * 
	 * @param clickable
	 *            True if the button should accept a click
	 */
	void setGetimgButtonClickable(boolean clickable) {
		getimgButton.setClickable(clickable);
	}

	/** Request the viewfinder to be invalidated. */
	void drawViewfinder() {
		viewfinderView.drawViewfinder();
	}

	@Override
	public void onShutterButtonClick(ShutterButton b) {
		if (isContinuousModeActive) {
			onShutterButtonPressContinuous();
		} else {
			if (handler != null) {
				handler.shutterButtonClick();
			}
		}
	}

	@Override
	public void onShutterButtonFocus(ShutterButton b, boolean pressed) {
		requestDelayedAutoFocus();
	}

	@Override
	public void onGetImageButtonClick(GetImageButton b) {
		// TODO Auto-generated method stub
		handler.getimageButtonClick();
	}

	/**
	 * Requests autofocus after a 350 ms delay. This delay prevents requesting
	 * focus when the user just wants to click the shutter button without
	 * focusing. Quick button press/release will trigger onShutterButtonClick()
	 * before the focus kicks in.
	 */
	private void requestDelayedAutoFocus() {
		// Wait 350 ms before focusing to avoid interfering with quick button
		// presses when
		// the user just wants to take a picture without focusing.
		cameraManager.requestAutoFocus(350L);
	}

	/**
	 * We want the help screen to be shown automatically the first time a new
	 * version of the app is run. The easiest way to do this is to check
	 * android:versionCode from the manifest, and compare it to a value stored
	 * as a preference.
	 */
	private boolean checkFirstLaunch() {
		return (PreferenceManager.getDefaultSharedPreferences(this).getInt(
				PreferencesActivity.KEY_HELP_VERSION_SHOWN, 0) == 0);
	}

	/**
	 * Returns a string that represents which OCR engine(s) are currently set to
	 * be run.
	 * 
	 * @return OCR engine mode
	 */
	String getOcrEngineModeName() {
		String ocrEngineModeName = "";
		String[] ocrEngineModes = getResources().getStringArray(
				R.array.ocrenginemodes);
		if (ocrEngineMode == TessBaseAPI.OEM_TESSERACT_ONLY) {
			ocrEngineModeName = ocrEngineModes[0];
		} else if (ocrEngineMode == TessBaseAPI.OEM_CUBE_ONLY) {
			ocrEngineModeName = ocrEngineModes[1];
		} else if (ocrEngineMode == TessBaseAPI.OEM_TESSERACT_CUBE_COMBINED) {
			ocrEngineModeName = ocrEngineModes[2];
		}
		return ocrEngineModeName;
	}

	/**
	 * Gets values from shared preferences and sets the corresponding data
	 * members in this activity.
	 */
	private void retrievePreferences() {
		prefs = PreferenceManager.getDefaultSharedPreferences(this);

		// Retrieve from preferences, and set in this Activity, the language
		// preferences
		PreferenceManager.setDefaultValues(this, R.xml.preferences, false);
		setSourceLanguage(prefs.getString(
				PreferencesActivity.KEY_SOURCE_LANGUAGE_PREFERENCE,
				CaptureActivity.DEFAULT_SOURCE_LANGUAGE_CODE));
		setTargetLanguage(prefs.getString(
				PreferencesActivity.KEY_TARGET_LANGUAGE_PREFERENCE,
				CaptureActivity.DEFAULT_TARGET_LANGUAGE_CODE));
		isTranslationActive = prefs.getBoolean(
				PreferencesActivity.KEY_TOGGLE_TRANSLATION, false);
		this.isEditResult = prefs.getBoolean(
				PreferencesActivity.KEY_REVERSE_IMAGE, false);

		// Retrieve from preferences, and set in this Activity, the capture mode
		// preference
		if (!prefs.getBoolean(PreferencesActivity.KEY_ONLINE_MODE,
				CaptureActivity.DEFAULT_ONLINE_MODE)) {
			isOnline = true;
			if (prefs.getBoolean(
					PreferencesActivity.KEY_OCR_TESSERACT_ONLY_MODE,
					CaptureActivity.DEFAULT_PRE_POST_PROCESSING)) {
				isPrePostProcessing = true;
			} else {
				isPrePostProcessing = false;
			}
		} else {
			isOnline = false;
			if (prefs.getBoolean(PreferencesActivity.KEY_CONTINUOUS_PREVIEW,
					CaptureActivity.DEFAULT_TOGGLE_CONTINUOUS)) {
				isContinuousModeActive = true;
			} else {
				isContinuousModeActive = false;
			}
		}

		// Retrieve from preferences, and set in this Activity, the page
		// segmentation mode preference
		String[] pageSegmentationModes = getResources().getStringArray(
				R.array.pagesegmentationmodes);
		String pageSegmentationModeName = prefs.getString(
				PreferencesActivity.KEY_PAGE_SEGMENTATION_MODE,
				pageSegmentationModes[0]);
		if (pageSegmentationModeName.equals(pageSegmentationModes[0])) {
			pageSegmentationMode = TessBaseAPI.PSM_AUTO_OSD;
		} else if (pageSegmentationModeName.equals(pageSegmentationModes[1])) {
			pageSegmentationMode = TessBaseAPI.PSM_AUTO;
		} else if (pageSegmentationModeName.equals(pageSegmentationModes[2])) {
			pageSegmentationMode = TessBaseAPI.PSM_SINGLE_BLOCK;
		} else if (pageSegmentationModeName.equals(pageSegmentationModes[3])) {
			pageSegmentationMode = TessBaseAPI.PSM_SINGLE_CHAR;
		} else if (pageSegmentationModeName.equals(pageSegmentationModes[4])) {
			pageSegmentationMode = TessBaseAPI.PSM_SINGLE_COLUMN;
		} else if (pageSegmentationModeName.equals(pageSegmentationModes[5])) {
			pageSegmentationMode = TessBaseAPI.PSM_SINGLE_LINE;
		} else if (pageSegmentationModeName.equals(pageSegmentationModes[6])) {
			pageSegmentationMode = TessBaseAPI.PSM_SINGLE_WORD;
		} else if (pageSegmentationModeName.equals(pageSegmentationModes[7])) {
			pageSegmentationMode = TessBaseAPI.PSM_SINGLE_BLOCK_VERT_TEXT;
		}

		// Retrieve from preferences, and set in this Activity, the OCR engine
		// mode
		String[] ocrEngineModes = getResources().getStringArray(
				R.array.ocrenginemodes);
		String ocrEngineModeName = prefs.getString(
				PreferencesActivity.KEY_OCR_ENGINE_MODE, ocrEngineModes[0]);
		if (ocrEngineModeName.equals(ocrEngineModes[0])) {
			ocrEngineMode = TessBaseAPI.OEM_TESSERACT_ONLY;
		} else if (ocrEngineModeName.equals(ocrEngineModes[1])) {
			ocrEngineMode = TessBaseAPI.OEM_CUBE_ONLY;
		} else if (ocrEngineModeName.equals(ocrEngineModes[2])) {
			ocrEngineMode = TessBaseAPI.OEM_TESSERACT_CUBE_COMBINED;
		}

		// Retrieve from preferences, and set in this Activity, the character
		// blacklist and whitelist
		characterBlacklist = OcrCharacterHelper.getBlacklist(prefs,
				sourceLanguageCodeOcr);
		characterWhitelist = OcrCharacterHelper.getWhitelist(prefs,
				sourceLanguageCodeOcr);

		prefs.registerOnSharedPreferenceChangeListener(listener);
	}

	/**
	 * Sets default values for preferences. To be called the first time this app
	 * is run.
	 */
	private void setDefaultPreferences() {
		prefs = PreferenceManager.getDefaultSharedPreferences(this);

		// Continuous preview
		prefs.edit()
				.putBoolean(PreferencesActivity.KEY_CONTINUOUS_PREVIEW,
						CaptureActivity.DEFAULT_TOGGLE_CONTINUOUS).commit();

		// Recognition language
		prefs.edit()
				.putString(PreferencesActivity.KEY_SOURCE_LANGUAGE_PREFERENCE,
						CaptureActivity.DEFAULT_SOURCE_LANGUAGE_CODE).commit();

		// Translation
		prefs.edit()
				.putBoolean(PreferencesActivity.KEY_TOGGLE_TRANSLATION,
						CaptureActivity.DEFAULT_TOGGLE_TRANSLATION).commit();

		// Translation target language
		prefs.edit()
				.putString(PreferencesActivity.KEY_TARGET_LANGUAGE_PREFERENCE,
						CaptureActivity.DEFAULT_TARGET_LANGUAGE_CODE).commit();

		// OCR Engine
		prefs.edit()
				.putString(PreferencesActivity.KEY_OCR_ENGINE_MODE,
						CaptureActivity.DEFAULT_OCR_ENGINE_MODE).commit();

		// Autofocus
		prefs.edit()
				.putBoolean(PreferencesActivity.KEY_AUTO_FOCUS,
						CaptureActivity.DEFAULT_TOGGLE_AUTO_FOCUS).commit();

		// Character blacklist
		prefs.edit()
				.putString(
						PreferencesActivity.KEY_CHARACTER_BLACKLIST,
						OcrCharacterHelper
								.getDefaultBlacklist(CaptureActivity.DEFAULT_SOURCE_LANGUAGE_CODE))
				.commit();

		// Character whitelist
		prefs.edit()
				.putString(
						PreferencesActivity.KEY_CHARACTER_WHITELIST,
						OcrCharacterHelper
								.getDefaultWhitelist(CaptureActivity.DEFAULT_SOURCE_LANGUAGE_CODE))
				.commit();

		// Page segmentation mode
		prefs.edit()
				.putString(PreferencesActivity.KEY_PAGE_SEGMENTATION_MODE,
						CaptureActivity.DEFAULT_PAGE_SEGMENTATION_MODE)
				.commit();

		// Reversed camera image
		prefs.edit()
				.putBoolean(PreferencesActivity.KEY_REVERSE_IMAGE,
						CaptureActivity.DEFAULT_TOGGLE_REVERSED_IMAGE).commit();

		// Light
		prefs.edit()
				.putBoolean(PreferencesActivity.KEY_TOGGLE_LIGHT,
						CaptureActivity.DEFAULT_TOGGLE_LIGHT).commit();
		// version
		prefs.edit().putInt(PreferencesActivity.KEY_HELP_VERSION_SHOWN, 1)
				.commit();
	}

	void displayProgressDialog() {
		// Set up the indeterminate progress dialog box
		indeterminateDialog = new ProgressDialog(this);
		indeterminateDialog.setTitle("Please wait");
		String ocrEngineModeName = getOcrEngineModeName();
		if (ocrEngineModeName.equals("Both")) {
			indeterminateDialog
					.setMessage("Performing OCR using Cube and Tesseract...");
		} else {
			indeterminateDialog.setMessage("Performing OCR using "
					+ ocrEngineModeName + "...");
		}
		indeterminateDialog.setCancelable(false);
		indeterminateDialog.show();
	}

	ProgressDialog getProgressDialog() {
		return indeterminateDialog;
	}

	/**
	 * Displays an error message dialog box to the user on the UI thread.
	 * 
	 * @param title
	 *            The title for the dialog box
	 * @param message
	 *            The error message to be displayed
	 */
	void showErrorMessage(String title, String message) {
		new AlertDialog.Builder(this).setTitle(title).setMessage(message)
				.setNeutralButton("OK", new DialogInterface.OnClickListener() {

					@Override
					public void onClick(DialogInterface dialog, int which) {
						CaptureActivity.this.finish();
					}
				}).show();
	}

	protected void onActivityResult(int requestCode, int resultCode, Intent data) {
		if (resultCode != RESULT_OK)
			return;
		switch (requestCode) {
		case CaptureActivityHandler.PICK_FROM_FILE:
			// handler.handleGettedImage(data.getData());
			Database.uriSource = data.getData();
			Intent intent = new Intent().setClass(this,
					ImageViewerActivity.class);
			intent.putExtra(Database.HEADER_SOURCE, Database.HEADER_SOURCE_URI);
			this.startActivityForResult(intent,
					CaptureActivityHandler.AFTER_IMG_HANDLING);// startActivity(intent);
			break;
		case CaptureActivityHandler.AFTER_IMG_HANDLING:
			// Bitmap dataBitmap = Database.bitmapSource;
			// Database.bitmapSource = null;
			// Database.uriSource = null;
			Bundle extras = data.getExtras();
			System.out.println("result extras");
			if (extras != null) {
				// retrieve a reference to the ImageView
				Bitmap dataBitmap = extras.getParcelable("data");
				if (dataBitmap != null) {
					this.handler.handleAfterProcessingImage(dataBitmap);
				} else {
					System.err.println("dataBitmap null");
				}
			}
			break;
		}
	}
}
